import { Event, EventHandler } from "./types";

export class CallbackList {

    private _callbacks: Function[] = [];
    private _contexts: any[] = [];

    public add(callback: Function, context: any = null): void {
        this._callbacks.push(callback);
        this._contexts.push(context);
    }

    public remove(callback: Function, context: any = null) {

        if (this._callbacks.length === 0) {
            return;
        }

        let findIndex = this._callbacks.indexOf(callback);
        if (findIndex > -1 && this._contexts[findIndex] === context) {
            this._callbacks.splice(findIndex, 1);
            this._contexts.splice(findIndex, 1);
        }

    }

    public invoke(...args: any[]) {

        if (this._callbacks.length === 0) {
            return;
        }

        const callbacks = this._callbacks.slice(0);
        const contexts = this._contexts.slice(0);

        for (let i = 0; i < callbacks.length; i++) {
            try {
                callbacks[i].apply(contexts[i], args);
            } catch (e) {
                console.error(e);
            }
        }

    }

    public isEmpty() {
        return this._callbacks.length === 0;
    }

    public dispose() {
        this._callbacks.splice(0);
        this._contexts.splice(0);
    }

}


export class Emitter<T, P> {

    private _event?: Event<T, P>;
    private _callbacks?: CallbackList;

    public get event(): Event<T, P> {

        if (!this._event) {
            this._event = (listener: EventHandler<T, P>, thisArg?: any) => {
                if (!this._callbacks) {
                    this._callbacks = new CallbackList();
                }
                this._callbacks.add(listener, thisArg);
            };
        }

        return this._event;

    }

    public fire(sender: T, arg: P): void {
        if (this._callbacks) {
            this._callbacks.invoke.call(this._callbacks, sender, arg);
        }
    }

    public dispose() {
        if (this._callbacks) {
            this._callbacks.dispose();
            this._callbacks = undefined;
        }
    }

}

